/**
 * Copyright (c) 2018-2023, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 *
 * Contains: Implementation template of a regular virtio-device
 *
 * Change Logs:
 * Date           Author            Notes
 * 2023-07-29     Shiroko           Init
 */

#include "nxos.h"

#ifdef CONFIG_NX_DRIVER_VIRTIO_TEMPLATE

#undef NX_LOG_MOD_NAME
#define NX_LOG_MOD_NAME "VIRTIO-TEMPLATE"

#include "base/log.h"

#define DRV_NAME "virtio template"
#define DEV_NAME "virtio_template"

#include "../virtio.h"

NX_PRIVATE NX_Error VirtioTemplateQueueNotifyHandler(NX_Device *device, VirtioQueue *queue, NX_U32 idx)
{
    VirtioTemplateDeviceExtension *deviceExtension = VirtioGetDevExtension(device, VirtioTemplateDeviceExtension);
    NX_Size                       used_size        = queue->used->ring[idx].len;
    // Reuse allocated descriptor chain
    VirtioQueuePutIntoAvailRing(queue, idx);
    VirtioSendNotification(device, queue->queue_index, idx);
    return NX_EOK;
}

NX_PRIVATE void VirtioTemplateCleanUp(NX_Device *device);

NX_PRIVATE NX_Error VirtioTemplateInit(NX_Driver *driver, NX_Device *device)
{
    NX_Error err                       = NX_EOK;
    NX_LOG_D("VirtIO Template device initialize for %s.", device->name);
    NX_U32   features[1];
    features[0] &= ~(0);   // Disabled Features
    features[0] &= 0;      // Enabled Features

    // Negotiate features & status
    if ((err = VirtioDeviceInitialize(device, features, 1)) != NX_EOK)
        return err;

    // Device-specified initialization
    VirtioQueueAlloc(device, 1);
    VirtioQueueInit(device, 0, CONFIG_NX_DRIVER_VIRTIO_SPLIT_QUEUE_SIZE);

    // Driver OK
    VirtioDeviceDriverOK(device);

    VirtioTemplateDeviceExtension *deviceExtension = NX_MemAlloc(sizeof(VirtioTemplateDeviceExtension));
    if (deviceExtension == NX_NULL) goto nomem_failed;
    VirtioGetExtension(device)->devExt = (void *) deviceExtension;

    VirtioTemplateDeviceConfig *deviceCfg = NX_MemAlloc(sizeof(VirtioTemplateDeviceConfig));
    if (deviceCfg == NX_NULL) goto nomem_failed;
    VirtioReadDeviceConfig(device, 0, deviceCfg, sizeof(VirtioTemplateDeviceConfig));
    deviceExtension->deviceConfig = deviceCfg;

    return NX_EOK;

nomem_failed:
    err = NX_ENOMEM;
failed:
    VirtioTemplateCleanUp(device);
    return err;
}

NX_PRIVATE void VirtioTemplateCleanUp(NX_Device *device)
{
    // Reset the device
    VirtioDeviceReset(device);
    // Cleanup VirtioQueue
    for (int i = 0; i < VirtioGetExtension(device)->queue_num; i++)
        VirtioQueueDestroy(device, i);
    VirtioQueueFree(device);

    VirtioTemplateDeviceExtension *deviceExtension = VirtioGetDevExtension(device, VirtioTemplateDeviceExtension);
    if (deviceExtension == NX_NULL) return;
    NX_MemFree(deviceExtension);
}

NX_PRIVATE NX_Error open(struct NX_Device *device, NX_U32 flags)
{
    return NX_EOK;
}

NX_PRIVATE NX_Error close(struct NX_Device *device)
{
    return NX_EOK;
}

NX_PRIVATE NX_Error read(struct NX_Device *device, void *buf, NX_Offset off, NX_Size len, NX_Size *outLen)
{
    return NX_EOK;
}

NX_PRIVATE NX_Error write(struct NX_Device *device, void *buf, NX_Offset off, NX_Size len, NX_Size *outLen)
{
    return NX_EOK;
}

NX_PRIVATE NX_Error control(struct NX_Device *device, NX_U32 cmd, void *arg)
{
    return NX_EOK;
}

NX_PRIVATE NX_Error mappable(struct NX_Device *device, NX_Size length, NX_U32 port, NX_Addr *outPhyAddr)
{
    return NX_EOK;
}

NX_PRIVATE NX_Error poll(struct NX_Device *device, NX_PollState *pState)
{
    return NX_EOK;
}


NX_PRIVATE NX_DriverOps virtioTemplateDriverOps = {
        .open = open,
        .close = close,
        .read = read,
        .write = write,
        .control = control,
        .mappable = mappable,
        .poll = poll
};


NX_PRIVATE VirtioDriverTableEntity virtioTemplateDriverTableEntity = {
        .deviceId = VIRTIO_DEVICE_ID_TEMPLATE,
        .driverName = "virtio-template",
        .deviceNameTemplate = "virtio_template_%d",
        .deviceType = NX_DEVICE_TYPE_TEMPLATE,
        .driverOps = &virtioTemplateDriverOps,
        .initHandler = VirtioTemplateInit,
        .cleanUpHandler = VirtioTemplateCleanUp
};

VirtioDriverTableEntity *VirtioDriverTableEntityForVirtioTemplate(void)
{
    return &virtioTemplateDriverTableEntity;
}

#else

#include "../virtio.h"

VirtioDriverTableEntity *VirtioDriverTableEntityForVirtioTemplate(void)
{
    return NX_NULL;
}

#endif // CONFIG_NX_DRIVER_VIRTIO_TEMPLATE
